package helper

import (
	"api/global"
	"crypto/md5"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"path"
	"reflect"
	"strings"

	"github.com/go-playground/validator/v10"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.uber.org/zap"
)

func GetObjectId() string {
	return primitive.NewObjectID().Hex()
}

func Diff(slice1, slice2 []string) (result []string) {
	var tmp = make(map[string]string)

	if (len(slice1) < 1) && len(slice1) < 1 {
		return
	}

	for _, v := range slice1 {
		tmp[v] = v
	}
	for _, v := range slice2 {
		if tmp[v] != "" {
			delete(tmp, v)
		} else {
			tmp[v] = v
		}
	}
	for _, v := range tmp {
		result = append(result, v)
	}

	return
}

func GetFilesOnDir(dirPath string) (files []string, err error) {
	fileInfos, err := ioutil.ReadDir(dirPath)
	if err != nil {
		return
	}
	for i := range fileInfos {
		fileExt := path.Ext(fileInfos[i].Name())
		fileNameOnly := strings.TrimSuffix(fileInfos[i].Name(), fileExt)
		files = append(files, fileNameOnly)
	}
	return
}

//LeftUpper Initial to capital
func LeftUpper(s string) string {
	if len(s) > 0 {
		return strings.ToUpper(string(s[0])) + s[1:]
	}
	return s
}

//LeftLower Initial to lowercase
func LeftLower(s string) string {
	if len(s) > 0 {
		return strings.ToLower(string(s[0])) + s[1:]
	}
	return s
}

func DirPathExist(path string) (err error) {
	fi, err := os.Stat(path)
	if err == nil {
		if fi.IsDir() {
			return
		}
		return errors.New("a file with the same name exists")
	}
	if os.IsNotExist(err) {
		return
	}
	return
}

func FilePathExist(path string) (err error) {
	fi, err := os.Stat(path)
	if err == nil {
		if fi.IsDir() {
			return errors.New("a directory with the same name exists")
		}
		return
	}
	if os.IsNotExist(err) {
		return
	}
	return
}

// WhthTrace write log with traceid
// example: helper.WithTrace(global.Logger).Info("example")
func WithTrace(logger *zap.Logger) *zap.Logger {
	return logger.With(zap.String("traceId", global.TraceId))
}

// VerifyParams verify params
func VerifyParams(s interface{}) (err error) {
	validate := validator.New()
	errs := validate.Struct(s)

	if errs != nil {
		for _, err := range errs.(validator.ValidationErrors) {
			fieldName := err.Field()
			field, ok := reflect.TypeOf(s).FieldByName(fieldName)
			if ok {
				verifyMsg := field.Tag.Get("verify_msg")
				if verifyMsg == "" {
					return err
				}
				return errors.New(verifyMsg)
			} else {
				return errors.New(fieldName + " not found")
			}
		}
	}

	return
}

func GenerateRandomString(n int) string {
	letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
	b := make([]rune, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}

	return string(b)
}

func Md5(str string) string {
	return fmt.Sprintf("%x", md5.Sum([]byte(str)))
}
